/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ClassDeclaration, EnumDeclaration, InterfaceDeclaration, SourceFile, TypeAliasDeclaration } from 'ts-morph';
import {
  ClassDetails, EnumDetails, EnumMemberInfo, InterfaceDetails,
  TypeAliasDetails, TypeDependency, TypeDetails, TypeKind
} from '..';
import { TsMorphUtil } from '../utils/TsMorphUtil';
import SerializationPathUtil from '../utils/SerializationPathUtil';

export class CustomTypeAnalyzer {
  private static readonly INSTANCE: CustomTypeAnalyzer = new CustomTypeAnalyzer();
  /** 类型查找缓存 <名称+路径，依赖地址> */
  private searchCache = new Map<string, TypeDependency | null>();

  private constructor() { }

  public static getInstance(): CustomTypeAnalyzer {
    return this.INSTANCE;
  }

  /**
   * 类型查找方法-实现本文件-->本模块-->跨模块三步走查找逻辑
   *
   * @param targetName 目标类型名称
   * @param sourceFile 起始源文件
   * @returns 查找结果，未找到时返回 null
   */
  public findDefinition(targetName: string, sourceFile: SourceFile): TypeDependency | null {
    const cacheKey = `${sourceFile.getFilePath()}:${targetName}`;

    // 检查缓存
    if (cacheKey && this.searchCache.has(cacheKey)) {
      const cached = this.searchCache.get(cacheKey);
      return cached !== undefined ? cached : null;
    }

    let result: TypeDependency | null;

    // 步骤1：在当前文件中查找
    result = this.findInCurrentFile(targetName, sourceFile);
    if (result) {
      if (cacheKey) {
        this.searchCache.set(cacheKey, result);
      }
      return result;
    }

    // 步骤2：根据导入语句查找本模块内的类型定义
    result = this.findInImports(targetName, sourceFile);
    if (result) {
      if (cacheKey) {
        this.searchCache.set(cacheKey, result);
      }
      return result;
    }

    // 步骤3：查找oh_modules中的跨模块类型定义
    result = this.findInCrossModule(targetName, sourceFile);
    if (result) {
      if (cacheKey) {
        this.searchCache.set(cacheKey, result);
      }
      return result;
    }

    // 缓存未找到的结果
    if (cacheKey) {
      this.searchCache.set(cacheKey, null);
    }
    return null;
  }

  public findClassDeclFromClassName(className: string, sourceFile: SourceFile): ClassDeclaration | InterfaceDeclaration | null {
    const res = this.findDefinition(className, sourceFile);
    if (res && res.typeKind === TypeKind.CLASS) {
      return (res.details as ClassDetails).classDecl;
    }
    if (res && res.typeKind === TypeKind.INTERFACE) {
      return (res.details as InterfaceDetails).interfaceDecl;
    }
    return null;
  }

  public clearCache(): void {
    this.searchCache.clear();
  }

  private findInCurrentFile(targetName: string, sourceFile: SourceFile): TypeDependency | null {
    const typeDetail = this.searchInSourceFile(targetName, sourceFile);
    if (typeDetail) {
      return {
        typeName: targetName,
        typeKind: typeDetail.kind,
        importPath: '',
        source: {
          type: 'local',
          sourceFilePath: sourceFile.getFilePath()
        },
        details: typeDetail
      };
    }
    return null;
  }

  private findInImports(targetName: string, sourceFile: SourceFile): TypeDependency | null {
    const imports = sourceFile.getImportDeclarations();

    for (const importDecl of imports) {
      // TODO:当前实现只处理命名导入，default导入和命名空间导入暂未支持
      const namedImports = importDecl.getNamedImports();
      const specifierValue = importDecl.getModuleSpecifierValue();

      // 检查是否在命名导入中找到了目标类型
      for (const namedImport of namedImports) {
        const importName = namedImport.getName();
        if (importName === targetName) {
          // 找到了导入的类型，现在需要解析文件路径
          const moduleSpecifier = importDecl.getModuleSpecifierValue();
          const resolvedPath = this.resolveImportPath(moduleSpecifier, sourceFile);

          if (resolvedPath && SerializationPathUtil.exist(resolvedPath)) {
            // 判断是文件还是目录
            if (SerializationPathUtil.isDictionary(resolvedPath)) {
              // 是目录，需要扫描目录下的所有 .ets 文件
              const result = this.searchTypeInDirectory(targetName, resolvedPath, specifierValue);
              if (result) {
                return result;
              }
            } else {
              // 是文件，加载并查找
              const result = this.searchTypeInFile(targetName, resolvedPath, specifierValue);
              if (result) {
                return result;
              }
            }
          }
        }
      }
    }

    return null;
  }

  /**
   * 在目录中扫描查找目标类型
   */
  private searchTypeInDirectory(targetName: string, dirPath: string, importPath: string): TypeDependency | null {
    const etsFiles = SerializationPathUtil.scanEtsFiles(dirPath);

    for (const filePath of etsFiles) {
      const result = this.searchTypeInFile(targetName, filePath, importPath);
      if (result) {
        return result;
      }
    }

    return null;
  }

  /**
   * 在文件中查找目标类型
   * 1. 先在文件中直接查找类型定义
   * 2. 如果找不到，解析 export { X } from './xxx' 导出语句，递归查找
   */
  private searchTypeInFile(targetName: string, filePath: string, importPath: string): TypeDependency | null {
    const sourceFile = this.getOrLoadSourceFile(filePath);
    if (!sourceFile) {
      return null;
    }

    // 1. 先在当前文件中直接查找类型定义
    const typeDetail = this.searchInSourceFile(targetName, sourceFile);
    if (typeDetail) {
      return {
        typeName: targetName,
        typeKind: typeDetail.kind,
        importPath: importPath,
        source: {
          type: 'imported',
          sourceFilePath: sourceFile.getFilePath(),
          originalImport: importPath,
          packageName: importPath
        },
        details: typeDetail
      };
    }

    // 2. 解析 re-export 语句: export { X } from './xxx'
    const exportDeclarations = sourceFile.getExportDeclarations();
    for (const exportDecl of exportDeclarations) {
      const namedExports = exportDecl.getNamedExports();

      for (const namedExport of namedExports) {
        // 检查导出名称是否匹配（考虑别名情况: export { A as B }）
        const exportedName = namedExport.getAliasNode()?.getText() || namedExport.getName();
        if (exportedName === targetName) {
          // 找到了 re-export，解析目标文件
          const moduleSpecifier = exportDecl.getModuleSpecifierValue();
          if (moduleSpecifier) {
            const resolvedPath = this.resolveImportPath(moduleSpecifier, sourceFile);
            if (resolvedPath && SerializationPathUtil.exist(resolvedPath)) {
              // 递归查找，使用原始名称（非别名）
              const originalName = namedExport.getName();
              if (SerializationPathUtil.isDictionary(resolvedPath)) {
                return this.searchTypeInDirectory(originalName, resolvedPath, importPath);
              } else {
                return this.searchTypeInFile(originalName, resolvedPath, importPath);
              }
            }
          }
        }
      }
    }

    return null;
  }

  private findInCrossModule(targetName: string, sourceFile: SourceFile): TypeDependency | null {
    const imports = sourceFile.getImportDeclarations();

    for (const importDecl of imports) {
      // TODO:当前实现只处理命名导入，default导入和命名空间导入暂未支持
      const namedImports = importDecl.getNamedImports();
      const specifierValue = importDecl.getModuleSpecifierValue();

      // 跳过相对路径导入（已在 findInImports 中处理）
      if (specifierValue.startsWith('./') || specifierValue.startsWith('../')) {
        continue;
      }

      // 检查是否在命名导入中找到了目标类型
      for (const namedImport of namedImports) {
        const importName = namedImport.getName();
        if (importName === targetName) {
          // 找到了导入的类型，现在需要解析文件路径
          const resolvedPath = this.resolveImportPath(specifierValue, sourceFile);

          if (resolvedPath && SerializationPathUtil.exist(resolvedPath)) {
            // 判断是文件还是目录
            if (SerializationPathUtil.isDictionary(resolvedPath)) {
              // 是目录，需要扫描目录下的所有 .ets 文件
              const result = this.searchTypeInDirectory(targetName, resolvedPath, specifierValue);
              if (result) {
                return result;
              }
            } else {
              // 是文件，使用 searchTypeInFile 支持 re-export 解析
              const result = this.searchTypeInFile(targetName, resolvedPath, specifierValue);
              if (result) {
                return result;
              }
            }
          }
        }
      }
    }
    return null;
  }

  private searchInSourceFile(targetName: string, sourceFile: SourceFile): TypeDetails | null {
    // 查找类定义
    const classes = sourceFile.getClasses();
    for (const classDecl of classes) {
      if (classDecl.getName() === targetName) {
        return this.parseClassDetails(classDecl);
      }
    }

    // 查找接口定义
    const interfaces = sourceFile.getInterfaces();
    for (const interfaceDecl of interfaces) {
      if (interfaceDecl.getName() === targetName) {
        return this.parseInterfaceDetails(interfaceDecl);
      }
    }

    // 查找枚举定义
    const enums = sourceFile.getEnums();
    for (const enumDecl of enums) {
      if (enumDecl.getName() === targetName) {
        return this.parseEnumDetails(enumDecl);
      }
    }

    // 查找类型定义
    const types = sourceFile.getTypeAliases();
    for (const typeAliasDecl of types) {
      if (typeAliasDecl.getName() === targetName) {
        return this.parseTypeAliasDetails(typeAliasDecl);
      }
    }

    return null;
  }

  private parseEnumDetails(enumDecl: EnumDeclaration): EnumDetails {
    const members: EnumMemberInfo[] = [];
    let valueType: 'string' | 'number' = 'number'; // 默认为数值类型
    let numericValue = 0; // 用于自动递增的数值

    const enumMembers = enumDecl.getMembers();
    for (const member of enumMembers) {
      const memberName = member.getName();
      let memberValue: string | number;
      let literalValue: string;

      const initializer = member.getInitializer();
      if (initializer) {
        // 有显式初始值
        const initText = initializer.getText().trim();

        // 判断是字符串还是数值
        if (initText.startsWith('"') && initText.endsWith('"')) {
          // 字符串枚举
          memberValue = initText.slice(1, -1); // 去掉引号
          literalValue = initText; // 保留引号
          valueType = 'string';
        } else if (initText.startsWith("'") && initText.endsWith("'")) {
          // 字符串枚举（单引号）
          memberValue = initText.slice(1, -1); // 去掉引号
          literalValue = `"${memberValue}"`; // 统一使用双引号
          valueType = 'string';
        } else {
          // 数值枚举
          const numValue = parseInt(initText, 10);
          if (!isNaN(numValue)) {
            memberValue = numValue;
            literalValue = numValue.toString();
            numericValue = numValue + 1; // 更新下一个自动值
          } else {
            // 复杂表达式，暂时当作字符串处理
            memberValue = initText;
            literalValue = `"${initText}"`;
            valueType = 'string';
          }
        }
      } else {
        // 没有显式初始值，使用自动递增的数值
        memberValue = numericValue;
        literalValue = numericValue.toString();
        numericValue++;
      }
      members.push({
        name: memberName,
        value: memberValue,
        literalValue
      });
    }
    return { kind: TypeKind.ENUM, valueType, members };
  }

  private parseClassDetails(classDecl: ClassDeclaration): ClassDetails {
    return {
      kind: TypeKind.CLASS,
      classDecl,
      isGeneric: classDecl.getTypeParameters().length > 0
    };
  }

  private parseInterfaceDetails(interfaceDecl: InterfaceDeclaration): InterfaceDetails {
    return {
      kind: TypeKind.INTERFACE,
      interfaceDecl,
      isGeneric: interfaceDecl.getTypeParameters().length > 0
    };
  }

  private parseTypeAliasDetails(typeAliasDecl: TypeAliasDeclaration): TypeAliasDetails {
    const aliasedType = typeAliasDecl.getTypeNodeOrThrow().getText();

    // 解析实际类型节点，填充 resolvedType
    const actualTypeNode = typeAliasDecl.getTypeNodeOrThrow();
    const { TypeHandlerRegistry } = require('../handlers/TypeHandlerRegistry');

    // 递归解析实际类型结构
    const resolvedType = TypeHandlerRegistry.getInstance().parseType(actualTypeNode, {
      depth: 0,
      genericParams: []
    });

    return { kind: TypeKind.TYPE, aliasedType, resolvedType };
  }

  private resolveImportPath(moduleSpecifier: string, sourceFile: SourceFile): string | null {
    const currentFilePath = sourceFile.getFilePath();
    const currentDir = SerializationPathUtil.dirname(currentFilePath);

    // 处理相对路径导入
    if (moduleSpecifier.startsWith('./') || moduleSpecifier.startsWith('../')) {
      const resolvedPath = SerializationPathUtil.pathResolve(currentDir, moduleSpecifier);

      // 尝试不同的文件扩展名
      const extensions = ['.ets', '.d.ets'];
      for (const ext of extensions) {
        const fullPath = resolvedPath + ext;
        if (SerializationPathUtil.exist(fullPath)) {
          return fullPath;
        }
      }
    } else {
      // 解析模块化路径：'common/ets/models/Product' -> moduleName='common', subPath='ets/models/Product'
      const firstSlashIndex = moduleSpecifier.indexOf('/');
      let moduleName: string;
      let subPath: string | null = null;

      if (firstSlashIndex > 0) {
        moduleName = moduleSpecifier.substring(0, firstSlashIndex);
        subPath = moduleSpecifier.substring(firstSlashIndex + 1);
      } else {
        moduleName = moduleSpecifier;
      }

      // 检查当前文件是否在目标模块内部（通过检查路径是否包含模块名）
      // 例如：当前文件在 oh_modules/common/Index.ets，导入 'common/ets/models/Product'
      // 应该直接在当前模块内解析，而不是再去 oh_modules 查找
      const currentModuleRoot = this.findCurrentModuleRoot(currentFilePath, moduleName);
      if (currentModuleRoot && subPath) {
        const subPathResolved = this.resolveModularSubPath(currentModuleRoot, subPath);
        if (subPathResolved) {
          return subPathResolved;
        }
      }

      // 跨模块导入：从当前文件目录开始向上递归查找oh_modules目录
      const ohModulesPath = this.findOhModulesDir(currentDir);
      if (ohModulesPath) {
        const moduleRoot = SerializationPathUtil.join(ohModulesPath, moduleName);

        if (SerializationPathUtil.exist(moduleRoot)) {
          // 如果有子路径，直接解析到具体文件
          if (subPath) {
            const subPathResolved = this.resolveModularSubPath(moduleRoot, subPath);
            if (subPathResolved) {
              return subPathResolved;
            }
          }

          // 优先查找模块根目录下的 Index.ets
          const indexFile = SerializationPathUtil.join(moduleRoot, 'Index.ets');
          if (SerializationPathUtil.exist(indexFile)) {
            return indexFile;
          }

          // 其次查找 src/main/ets 目录
          const srcEtsPath = SerializationPathUtil.join(moduleRoot, 'src/main/ets');
          if (SerializationPathUtil.exist(srcEtsPath)) {
            return srcEtsPath;
          }
        }
      }
    }

    return null;
  }

  /**
   * 查找当前文件所在的模块根目录（如果模块名匹配）
   * 用于处理模块内部的模块化路径导入
   */
  private findCurrentModuleRoot(currentFilePath: string, targetModuleName: string): string | null {
    // 向上遍历目录，查找包含模块名的目录
    let currentDir = SerializationPathUtil.dirname(currentFilePath);

    while (currentDir) {
      const dirName = SerializationPathUtil.basename(currentDir);

      // 检查目录名是否匹配目标模块名
      if (dirName === targetModuleName) {
        // 验证这是一个有效的模块根目录（有 Index.ets 或 src/main/ets）
        const indexFile = SerializationPathUtil.join(currentDir, 'Index.ets');
        const srcMainEts = SerializationPathUtil.join(currentDir, 'src/main/ets');
        if (SerializationPathUtil.exist(indexFile) || SerializationPathUtil.exist(srcMainEts)) {
          return currentDir;
        }
      }

      const parentDir = SerializationPathUtil.dirname(currentDir);
      if (parentDir === currentDir) {
        break;
      }
      currentDir = parentDir;
    }

    return null;
  }

  /**
   * 解析模块化子路径
   * 'ets/models/Product' -> 'src/main/ets/models/Product.ets'
   */
  private resolveModularSubPath(moduleRoot: string, subPath: string): string | null {
    // 模块化路径格式：'ets/models/Product' 对应实际路径 'src/main/ets/models/Product.ets'
    // 需要在子路径前添加 'src/main/' 前缀
    const extensions = ['.ets', '.d.ets'];

    // 尝试方案1：直接在 src/main 下查找
    const srcMainPath = SerializationPathUtil.join(moduleRoot, 'src/main', subPath);
    for (const ext of extensions) {
      const fullPath = srcMainPath + ext;
      if (SerializationPathUtil.exist(fullPath)) {
        return fullPath;
      }
    }

    // 尝试方案2：直接在模块根目录下查找（某些模块可能没有 src/main 结构）
    const directPath = SerializationPathUtil.join(moduleRoot, subPath);
    for (const ext of extensions) {
      const fullPath = directPath + ext;
      if (SerializationPathUtil.exist(fullPath)) {
        return fullPath;
      }
    }

    return null;
  }

  private findOhModulesDir(startDir: string): string | null {
    let currentDir = startDir;

    while (currentDir) {
      const ohModulesPath = SerializationPathUtil.join(currentDir, 'oh_modules');
      if (SerializationPathUtil.exist(ohModulesPath) && SerializationPathUtil.isDictionary(ohModulesPath)) {
        return ohModulesPath;
      }

      const parentDir = SerializationPathUtil.dirname(currentDir);
      // 如果已经到根目录，停止查找
      if (parentDir === currentDir) {
        break;
      }
      currentDir = parentDir;
    }

    return null;
  }

  private getOrLoadSourceFile(filePath: string): SourceFile | null {
    const existingFile = TsMorphUtil.getProject().getSourceFile(filePath);
    if (existingFile) {
      return existingFile;
    }

    if (SerializationPathUtil.exist(filePath)) {
      return TsMorphUtil.getProject().addSourceFileAtPath(filePath);
    }

    return null;
  }
}
